#include "clib_coded_base64.h"

size_t clib_coded_base64_encode(clib_uint8_t const *in_data, size_t in_size, char *out_data, size_t out_size) {
    clib_check_if_true_return_value(in_data == NULL, 0);
    clib_check_if_true_return_value(out_data == NULL, 0);
    clib_check_if_true_return_value(in_size >= UINT32_MAX / 4, 0);
    clib_check_if_true_return_value(out_size < CLIB_CODED_BASE64_OUTPUT_MIN(in_size), 0);
    static char const table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
    char *op = out_data;
    clib_uint32_t bits = 0;
    clib_int64_t left = (clib_int64_t)in_size;
    clib_int64_t shift = 0;
    while (left) {
        bits = (bits << 8) + *in_data++;
        left--;
        shift += 8;
        do {
            *op++ = table[(bits << 6 >> shift) & 0x3f];
            shift -= 6;
        } while (shift > 6 || (left == 0 && shift > 0));
    }
    while ((op - out_data) & 3) {
        *op++ = '=';
    }
    *op = '\0';
    return (op - out_data);
}


size_t clib_coded_base64_decode(char const *in_data, size_t in_size, clib_uint8_t *out_data, size_t out_size) {
    clib_check_if_true_return_value(in_data == NULL, 0);
    clib_check_if_true_return_value(out_data == NULL, 0);
    static clib_uint8_t table[] =
            {
                    0x3e, 0xff, 0xff, 0xff, 0x3f, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0xff,
                    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
                    0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19,
                    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23,
                    0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33
            };
    clib_int32_t i = 0;
    clib_int32_t v = 0;
    clib_uint8_t *op = out_data;
    size_t tn = ((sizeof((table)) / sizeof((table)[0])));
    for (i = 0; i < in_size && in_data[i] && in_data[i] != '='; i++) {
        clib_uint32_t idx = in_data[i] - 43;
        if (idx >= tn || table[idx] == 0xff) {
            return 0;
        }
        v = (v << 6) + table[idx];
        if (i & 3) {
            if (op - out_data < out_size) *op++ = v >> (6 - 2 * (i & 3));
        }
    }
    return (op - out_data);
}



